import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

class SPUtils {
  static SPUtils? _instance;
  static SharedPreferences? _prefs;

  // 私有构造函数
  SPUtils._();

  // 单例模式
  static Future<SPUtils> getInstance() async {
    _instance ??= SPUtils._();
    _prefs ??= await SharedPreferences.getInstance();
    return _instance!;
  }

  /// 保存字符串
  Future<bool> setString(String key, String value) async {
    return _prefs?.setString(key, value) ?? Future.value(false);
  }

  /// 获取字符串
  String getString(String key, {String defValue = ''}) {
    return _prefs?.getString(key) ?? defValue;
  }

  /// 保存整型
  Future<bool> setInt(String key, int value) async {
    return _prefs?.setInt(key, value) ?? Future.value(false);
  }

  /// 获取整型
  int getInt(String key, {int defValue = 0}) {
    return _prefs?.getInt(key) ?? defValue;
  }

  /// 保存布尔值
  Future<bool> setBool(String key, bool value) async {
    return _prefs?.setBool(key, value) ?? Future.value(false);
  }

  /// 获取布尔值
  bool getBool(String key, {bool defValue = false}) {
    return _prefs?.getBool(key) ?? defValue;
  }

  /// 保存浮点数
  Future<bool> setDouble(String key, double value) async {
    return _prefs?.setDouble(key, value) ?? Future.value(false);
  }

  /// 获取浮点数
  double getDouble(String key, {double defValue = 0.0}) {
    return _prefs?.getDouble(key) ?? defValue;
  }

  /// 保存字符串列表
  /// 保存字符串列表（可选去重，默认去重）
  Future<bool> setStringList(
    String key,
    List<String> value, {
    bool deduplicate = true,
  }) async {
    // 如果需要去重，就通过 Set 去除重复元素
    final List<String> finalList = deduplicate ? value.toSet().toList() : value;

    return _prefs?.setStringList(key, finalList) ?? Future.value(false);
  }

  /// 获取字符串列表
  List<String> getStringList(String key, {List<String> defValue = const []}) {
    return _prefs?.getStringList(key) ?? defValue;
  }

  /// 向指定 key 追加字符串列表（若 key 不存在则新建）
  /// [deduplicate] 是否去重，默认为 true
  Future<bool> appendStringList(
    String key,
    List<String> value, {
    bool deduplicate = true,
  }) async {
    try {
      // 获取当前存储的列表（若不存在则返回空列表）
      final List<String> existingList = getStringList(key);

      // 合并新旧列表
      List<String> mergedList = [...existingList, ...value];

      // 如果需要去重，则转换为 Set 再转回 List
      if (deduplicate) {
        mergedList = mergedList.toSet().toList();
      }

      // 保存合并后的列表
      return _prefs?.setStringList(key, mergedList) ?? Future.value(false);
    } catch (e) {
      return false; // 发生错误时返回 false
    }
  }

  /// 向指定 key 追加字符串列表（若 key 不存在则新建）
  /// [deduplicate] 是否去重，默认为 true
  Future<bool> headStringList(
    String key,
    List<String> value, {
    bool deduplicate = true,
  }) async {
    try {
      // 获取当前存储的列表（若不存在则返回空列表）
      final List<String> existingList = getStringList(key);

      // 合并新旧列表
      List<String> mergedList = [...value, ...existingList];

      // 如果需要去重，则转换为 Set 再转回 List
      if (deduplicate) {
        mergedList = mergedList.toSet().toList();
      }

      // 保存合并后的列表
      return _prefs?.setStringList(key, mergedList) ?? Future.value(false);
    } catch (e) {
      return false; // 发生错误时返回 false
    }
  }

  /// 删除指定键
  Future<bool> remove(String key) async {
    return _prefs?.remove(key) ?? Future.value(false);
  }

  /// 清空所有数据
  Future<bool> clear() async {
    return _prefs?.clear() ?? Future.value(false);
  }

  /// 检查是否包含某个键
  bool containsKey(String key) {
    return _prefs?.containsKey(key) ?? false;
  }

  /// 自动序列化/反序列化对象（需要引入dart:convert）
  Future<bool> setObject(String key, Map<String, dynamic> value) async {
    return setString(key, jsonEncode(value));
  }

  Map<String, dynamic> getObject(String key) {
    String jsonStr = getString(key);
    return jsonStr.isEmpty ? {} : jsonDecode(jsonStr);
  }

  /// 带过期时间的存储
  Future<bool> setStringWithExpiry(
    String key,
    String value,
    Duration expiryDuration,
  ) async {
    final expiryTime = DateTime.now()
        .add(expiryDuration)
        .millisecondsSinceEpoch;
    await setString(key, value);
    return setString('${key}_expiry', expiryTime.toString());
  }

  String getStringWithExpiry(String key, {String defValue = ''}) {
    final expiryStr = getString('${key}_expiry');
    if (expiryStr.isEmpty) return defValue;

    final expiryTime = int.tryParse(expiryStr) ?? 0;
    if (DateTime.now().millisecondsSinceEpoch > expiryTime) {
      remove(key);
      remove('${key}_expiry');
      return defValue;
    }
    return getString(key, defValue: defValue);
  }
}

extension ArrayStorageExtensions on SPUtils {
  Future<bool> addObjectArray<T>(
    String key,
    List<T> newArray, {
    bool Function(T, T)? isDuplicate,
    T Function(T existing, T newItem)? mergeStrategy,
    T Function(Map<String, dynamic>)? fromMap,
    Map<String, dynamic> Function(T)? toMap,
  }) async {
    try {
      final existingArray = getObjectArray(key, fromMap: fromMap);

      final combinedArray = _mergeArrays(
        existingArray,
        newArray,
        isDuplicate: isDuplicate,
        mergeStrategy: mergeStrategy,
      );

      return await setObjectArray(key, combinedArray, toMap: toMap);
    } catch (e) {
      return false;
    }
  }

  List<T> getObjectArray<T>(
    String key, {
    T Function(Map<String, dynamic>)? fromMap,
    List<T> defValue = const [],
  }) {
    final jsonStr = getString(key);
    if (jsonStr.isEmpty) return defValue;

    try {
      final list = jsonDecode(jsonStr) as List;
      if (fromMap == null) {
        return List<T>.from(list);
      } else {
        return list.map((e) => fromMap(e)).toList();
      }
    } catch (e) {
      return defValue;
    }
  }

  Future<bool> setObjectArray<T>(
    String key,
    List<T> array, {
    Map<String, dynamic> Function(T)? toMap,
  }) async {
    try {
      if (toMap == null) {
        return await setString(key, jsonEncode(array));
      } else {
        final jsonList = array.map((e) => toMap(e)).toList();
        return await setString(key, jsonEncode(jsonList));
      }
    } catch (e) {
      return false;
    }
  }

  List<T> _mergeArrays<T>(
    List<T> existing,
    List<T> incoming, {
    bool Function(T, T)? isDuplicate,
    T Function(T existing, T newItem)? mergeStrategy,
  }) {
    final result = [...existing];
    for (var newItem in incoming) {
      final duplicateIndex = isDuplicate == null
          ? result.indexOf(newItem)
          : result.indexWhere((e) => isDuplicate(e, newItem));

      if (duplicateIndex == -1) {
        result.add(newItem);
      } else if (mergeStrategy != null) {
        result[duplicateIndex] = mergeStrategy(result[duplicateIndex], newItem);
      }
    }
    return result;
  }
}
